﻿using RHLogReader.Model;
using System;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text.RegularExpressions;

namespace RHLogReader.Classes
{
    public class LogParser
    {
        private ObservableCollection<Message> _Messages;
        public ObservableCollection<Message> Messages
        {
            get { return _Messages; }
            set { _Messages = value; }
        }

        private ObservableCollection<Character> _Characters;
        public ObservableCollection<Character> Characters
        {
            get { return _Characters; }
            set { _Characters = value; }
        }

        private ObservableCollection<Channel> _Channels;
        public ObservableCollection<Channel> Channels
        {
            get { return _Channels; }
            set { _Channels = value; }
        }

        private ObservableCollection<RHLogReader.Model.Group> _MessageGroups;
        public ObservableCollection<RHLogReader.Model.Group> MessageGroups
        {
            get { return _MessageGroups; }
            set { _MessageGroups = value; }
        }

        private Character messageCharacter;
        private DateTime messageDate;
        private Channel messageChannel;
        private string messageText;
        private bool messageVisibility;
        private Message message;

        private string logContent;

        public LogParser(string logContent)
        {
            this.logContent = logContent;
        }

        public MessageBox Parse()
        {
            Messages = new ObservableCollection<Message>();
            Characters = new ObservableCollection<Character>();
            Channels = new ObservableCollection<Channel>();
            MessageGroups = new ObservableCollection<RHLogReader.Model.Group>() { new RHLogReader.Model.Group() { Name = "All" } };

            foreach (var line in logContent.Split('\n'))
            {
                if (line != null && line.CompareTo("") != 0)
                    Messages.Add(ParseText(line));
            }

            return new MessageBox() { Messages = this.Messages, Characters = this.Characters, Channels = this.Channels, Groups = this.MessageGroups };
        }

        private Message ParseText(string text)
        {
            messageVisibility = false;

            // Get date for line
            int daySeparator = text.IndexOf('/');
            int month = Int32.Parse(text.Substring(0, daySeparator));

            int monthSeparator = text.IndexOf(" ");
            int day = Int32.Parse(text.Substring(daySeparator + 1, monthSeparator - daySeparator));

            int hour = Int32.Parse(text.Substring(monthSeparator + 1, 2));
            int minute = Int32.Parse(text.Substring(monthSeparator + 4, 2));
            int second = Int32.Parse(text.Substring(monthSeparator + 7, 2));

            messageDate = new DateTime(DateTime.Now.Year, month, day, hour, minute, second);

            int dateSeparator = text.IndexOf("  ");
            string lineToBeParsed = text.Substring(dateSeparator + 2);

            /* Recognize character name
             * Types for Character = "You":
             * Joined Channel
             * To 
             * You 
             * 
             * Types for Character = "System":
             * |Hchannel
             * 
             * Types to ignore:
             * Changed Channel:
             * 
             * Channel types:
             * says: - Say
             * whispers: - Whisper
             * To <imie>: - Whisper
             * yells: - Yell
             * Imie bez separatorow - Emote
             * [5. Noir] - kanal Noir
             * |Hchannel:GUILD|h[Guild]|h - Guild
             * |Hchannel:PARTY|h[Party]|h - Party
            */

            // Say
            if (lineToBeParsed.Contains("says:"))
            {
                messageChannel = SetChannel("Say");

                GetBasicData(lineToBeParsed, " says: ");
            }
            // Whisp from
            else if (lineToBeParsed.Contains("whispers:"))
            {
                messageChannel = SetChannel("Whisp");

                GetBasicData(lineToBeParsed, " whispers: ");
            }
            // Whisp to
            else if (Regex.IsMatch(lineToBeParsed, "^To"))
            {
                messageChannel = SetChannel("Whisp");

                GetBasicData(lineToBeParsed, ": ");
            }
            // Yell
            else if (lineToBeParsed.Contains("yells:"))
            {
                messageChannel = SetChannel("Yell");

                GetBasicData(lineToBeParsed, " yells: ");
            }
            // Guild
            else if (lineToBeParsed.Contains("|Hchannel:GUILD|h[Guild]|h"))
            {
                messageChannel = SetChannel("Guild");

                lineToBeParsed = lineToBeParsed.Substring(27);

                GetBasicData(lineToBeParsed, ": ");
            }
            // Party member
            else if (lineToBeParsed.Contains("|Hchannel:PARTY|h[Party]|h"))
            {
                messageChannel = SetChannel("Party");

                lineToBeParsed = lineToBeParsed.Substring(27);

                GetBasicData(lineToBeParsed, ": ");
            }
            // Party leader
            else if (lineToBeParsed.Contains("|Hchannel:PARTY|h[Party Leader]|h"))
            {
                messageChannel = SetChannel("Party");

                lineToBeParsed = lineToBeParsed.Substring(33);

                GetBasicData(lineToBeParsed, ": ");
            }
            // najpierw rozpoznawanie kanalu
            else if (lineToBeParsed.StartsWith("[") && Regex.IsMatch(lineToBeParsed, @"^[[]\d+"))
            {
                messageChannel = SetChannel(lineToBeParsed.Substring(lineToBeParsed.IndexOf(" ") + 1, lineToBeParsed.IndexOf("]") - lineToBeParsed.IndexOf(" ") - 1));

                lineToBeParsed = lineToBeParsed.Substring(lineToBeParsed.IndexOf("]") + 2);

                if (Regex.IsMatch(lineToBeParsed, @"^\S+[:]"))
                    GetBasicData(lineToBeParsed, ": ");
                else
                    GetBasicData(lineToBeParsed, " ");
            }
            // pozniej cala reszta. Rozpoznawac komunikaty systemowe i rozdzielac je od emot
            // Emote
            else if (!IsSystemMessage(lineToBeParsed) && !IsUserSystemMessage(lineToBeParsed))
            {
                /* System communicates
                 * 
                |Hchannel:
                Joined Channel:
                Changed Channel:
                Left Channel:
                Looting changed to Group Loot.
                Loot threshold set to Uncommon.
                1 player total
                No player named
                 * 
                 * */

                messageChannel = SetChannel("Emote");
                GetBasicData(lineToBeParsed, " ");
            }
            // User system message
            else
            {
                messageChannel = SetChannel("Other");
                messageVisibility = false;
                messageText = lineToBeParsed;
            }

            return new Message(messageDate, messageCharacter, messageText, messageChannel, messageVisibility, MessageGroups.First());
        }

        bool IsSystemMessage(string line)
        {
            if (line.StartsWith("|Hchannel:") ||
                line.Contains("Channel:") ||
                line.StartsWith("Loot") ||
                line.Contains(" player "))
                return true;

            return false;
        }

        bool IsUserSystemMessage(string line)
        {
            return false;
        }

        void GetBasicData(string lineToBeParsed, string channelTemplate)
        {
            int channelSeparator = lineToBeParsed.IndexOf(channelTemplate);
            if (channelSeparator > 0)
            {
                messageCharacter = SetCharacter(lineToBeParsed.Substring(0, channelSeparator));

                messageText = lineToBeParsed.Substring(channelSeparator + channelTemplate.Length);
                messageVisibility = true;//!IsNPC(Character);
            }
        }

        Channel SetChannel(string name)
        {
            var foundChannels = Channels.Where(x => x.Name.CompareTo(name) == 0);

            if (foundChannels != null && foundChannels.Count() > 0)
            {
                return foundChannels.First();
            }
            else
            {
                Channels.Add(new Channel() { Name = name, IsSelected = true });
                return Channels.Last();
            }
        }

        Character SetCharacter(string name)
        {
            Regex rgx = new Regex("[^A-Za-z0-9 _]");

            var foundCharacters = Characters.Where(x => rgx.Replace(x.Name, "").CompareTo(rgx.Replace(name, "")) == 0);// x.Name.CompareTo(name) == 0);

            if (foundCharacters != null && foundCharacters.Count() > 0)
            {
                return foundCharacters.First();
            }
            else
            {
                Characters.Add(new Character() { Name = name });
                return Characters.Last();
            }
        }
    }
}
